package com.example.demo111.service.impl;

import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;


public class MainClass
{
	public static final int BASE_K = 5;

	public static final String OUTPUT_PATH = "D:/";

	public static final String DATA_SET_NAME = "PDE.csv";
	public static final String TRAINING_DATA = "train.csv";
	public static final String TEST_DATA = "test.csv";
	public static final String OUTPUT_DATA = "output.csv";

	public static void main(String[] args) throws Exception
	{
		splitDataSet(DATA_SET_NAME, 200, OUTPUT_PATH);
		DataUtil util = DataUtil.getInstance();
		//获得训练集和测试集
		List<DataNode> trainList = util.getDataList(OUTPUT_PATH + TRAINING_DATA, ",");
		List<DataNode> testList = util.getDataList(OUTPUT_PATH + TEST_DATA, ",");
		int K = BASE_K;
		KnnClassifier classifier = new KnnClassifier(K, trainList);
		BufferedWriter output = new BufferedWriter(new FileWriter(new File(
				OUTPUT_PATH + OUTPUT_DATA)));
		int typeCount = util.getTypeCount();
		int[] count = new int[typeCount];

		int hitCount = 0;

		for (int i = 0; i < testList.size();)
		{
			for (int m = 0; m < typeCount; m++)
				count[m] = 0;
			DataNode test = testList.get(i);
			classifier.setK(K);
			KNode[] nodes = classifier.getKNN(test, 2);
			for (int j = 0; j < nodes.length; j++)
				count[nodes[j].getType()]++;
			int type = -1;
			int max = -1;
			for (int j = 0; j < typeCount; j++)
			{
				if (count[j] > max)
				{
					max = count[j];
					type = j;
				} else if (count[j] == max)
				{
					// 存在两个类型分个数相同，无法判断属于哪个类型，增加K的值继续从该节点开始
					type = -1;
					K++;
					break;
				}
			}
			if (type == -1)
				continue;
			else
			{
				i++;
				K = BASE_K;
			}
			//将分类结果写入文件
			List<Float> attribs = test.getAttribs();
			for (int n = 0; n < attribs.size(); n++)
			{
				output.write(attribs.get(n) + ",");
				output.flush();
			}
			output.write(util.getTypeName(type) + "\n");
			if (util.getTypeName(type).equals(util.getTypeName(test.getType()))) {
				hitCount++;
			}
			output.flush();
		}
		System.out.println(String.format("准确率:%.2f%%", (float) hitCount / testList.size() * 100));
		output.close();

	}

	/**
	 * 将数据集划分为训练集和测试集，随机划分
	 * 
	 * @param filePath
	 *            数据集文件路径
	 * @param testCount
	 *            测试集个数
	 * @param outputPath
	 *            输出路径
	 * @throws Exception
	 */
	/*public static void splitDataSet(String filePath, int testCount,
			String outputPath) throws Exception
	{
		BufferedWriter trainFile = new BufferedWriter(new FileWriter(new File(
				outputPath + TRAINING_DATA)));
		BufferedWriter testFile = new BufferedWriter(new FileWriter(new File(
				outputPath + TEST_DATA)));
		BufferedReader input = new BufferedReader(new FileReader(new File(
				filePath)));
		List<String> lines = new ArrayList<String>();
		String line = null;
		//将所有数据读取到一个List里
		while ((line = input.readLine()) != null)
			lines.add(line);
		//遍历一次List，每次产生一个随机序号，将该随机序号和当前序号内容进行交换
		for (int i = 0; i < lines.size(); i++)
		{
			int ran = (int) (Math.random() * lines.size());
			String temp = lines.get(i);
			lines.set(i, lines.get(ran));
			lines.set(ran, temp);
		}
		int i = 0;
		//将指定数目的测试集写进test.txt中
		for (; i < testCount; i++)
		{
			testFile.write(lines.get(i) + "\n");
			testFile.flush();
		}
		//剩余的写进train.txt中
		for (; i < lines.size(); i++)
		{
			trainFile.write(lines.get(i) + "\n");
			trainFile.flush();
		}
		testFile.close();
		trainFile.close();
	}*/

	public static void splitDataSet(String filePath, int testCount,
									String outputPath) throws Exception {
		File trainFile = new File(outputPath + TRAINING_DATA);
		File testFile = new File(outputPath + TEST_DATA);

		BufferedWriter trainBuffer = new BufferedWriter(new FileWriter(trainFile,true));
		BufferedWriter testBuffer = new BufferedWriter(new FileWriter(testFile,true));

		CsvReader reader = new CsvReader(filePath, ',', StandardCharsets.UTF_8);

		CsvWriter trainWrite = new CsvWriter(trainBuffer, ',');
		CsvWriter testWrite = new CsvWriter(testBuffer, ',');

		// 跳过表头
		reader.readHeaders();

		List<String> lines = new ArrayList<>();

		while(reader.readRecord()) {
			String[] values = reader.getValues();
			lines.add(String.join(",", values));
		}

		for (int i = 0; i < lines.size(); i++)
		{
			int ran = (int) (Math.random() * lines.size());
			String temp = lines.get(i);
			lines.set(i, lines.get(ran));
			lines.set(ran, temp);
		}
		int i = 0;
		//将指定数目的测试集写进test.txt中
		for (; i < testCount; i++)
		{
			trainWrite.writeRecord(lines.get(i).split(","));
			trainWrite.flush();
		}
		trainWrite.endRecord();
		//剩余的写进train.txt中
		for (; i < lines.size(); i++)
		{
			testWrite.writeRecord(lines.get(i).split(","));
			testWrite.flush();
		}
		testWrite.endRecord();

		trainWrite.flush();
		testWrite.flush();

		trainWrite.close();
		testWrite.close();
		reader.close();
	}

}
